import sys

__author__ = 'lxb'

class xpfsm_ordereddict:
  """Ordered dictionary implementation."""

  # Define the minimum functionality we need for our application.
  # Easiser would be to subclass from UserDict.DictMixin, and only
  # define __getitem__, __setitem__, __delitem__, and keys, but that's
  # not as portable.  We don't need to define much more, so we just do.

  def __init__(self):
    self._dict = {}
    self._keys = []

  def __getitem__(self, key):
    return self._dict[key]

  def __setitem__(self, key, value):
    if key not in self._keys:
      self._keys.append(key)
    self._dict[key] = value

  def __delitem__(self, key):
    self._keys.remove(key)
    del self._dict[key]

  def keys(self):
    return self._keys

  # Below are all we have to define in addition to what DictMixin would need
  def __len__(self):
    return len(self.keys())

  def __contains__(self, key):
    return self.has_key(key)

  def __iter__(self):
    # It's not as portable -- though it would be more space-efficient -- to do
    #   for k in self.keys(): yield k
    return iter(self.keys())

class xpfsm_state:

    name = None
    transitions = []
    def __init__ (self):
        pass

class xpfsm_transition:

    event = None
    dest = None
    src = None

    def __init__ (self, event, src, dest):
        self.event = event
        self.dest = dest
        self.src = src

class xpfsm_config:
    events = {}
    transitions = []
    states = xpfsm_ordereddict()
    statemachine = {}
    name = None
    comment = None

    def __init__(self):
        pass

    def addEvent(self, name, expression):
        self.events[name] = expression

    def addState(self, **dict):
        state = xpfsm_state();
        state.name = dict['name']
        state_transitions = []

        for (event, dest) in dict['transitions']:
            transition = xpfsm_transition(event, state.name, dest)
            state_transitions.append(transition)

        self.transitions.extend(state_transitions)
        state.transitions = state_transitions
        self.states[state.name] = state

    def load(self, filename):
        self.statemachine['event'] = self.addEvent
        self.statemachine['state'] = self.addState
        execfile(filename, self.statemachine)
        self.name = self.statemachine['name']
        self.comment = self.statemachine['comment']

class xpfsm_generator:

    _config = None
    def __init__ (self, config):
        self._config = config

    def generate(self):
        print '\n'.join((self._generateHeader(),
            self._generateNumStates(),
            self._generateStatesEnum(),
            self._generateTransitionTable()))

    def _stateName(self, st):
        return '%s_STATE_%s' % (self._config.name.upper(), st.upper())

    def _generateEnum(self, n, l):
        return 'enum %s {\n%s\n}; \n' % (n, ',\n'.join(['  ' + e for e in l]))

    def _generateStruct(self, n, t, d):
        return "%s %s[] = {\n%s\n};\n" % (t, n, ',\n'.join(['  ' + e for e in d]))

    def _expandExpression(self, expression):
        def _expandRange(start, end):
            return [chr(c) for c in range(ord(start), ord(end) + 1)]

        def _listNext(input_list):
            if input_list:
                return input_list.pop(0)
            else:
                return None

        out = []
        if expression == '[:default:]':
            out = [chr(c) for c in range(0, 255)]
            return ''.join(out)

        chars = [c for c in expression]  # list o characters in the expression.
        current = _listNext(chars)
        while current:
            next = _listNext(chars)
            if next == '-':
                next = _listNext(chars)
                if next:
                    out.extend(_expandRange(current, next))
                else:
                    out.append(current)
                    out.append('-')
                current = _listNext(chars)
            else:
                out.append(current)
                current = next
        return ''.join(out)

    def _generateHeader(self):
        out = []
        if self._config.comment:
            out.append('/* ' + self._config.comment)
        else:
            out.append(' Statemachine definition for ' + self._config.name)
        out.append('* Auto generated by xpgenfsm.py. Don\'t modify it !')
        out.append('*/')
        return '\n'.join(out)

    def _generateNumStates(self):
        return "#define %s_NUM_STATES %s" % (self._config.name.upper(),
                                              str(len(self._config.states) + 1))

    def _generateStatesEnum(self):
        list = []

        for state in self._config.states:
            list.append(self._stateName(state))
        return self._generateEnum(self._config.name.lower() + '_state_enum', list)

    def _generateTransitionTable(self):
        out = []
        default_state = 'STATEMACHINE_ERROR'
        state_table = {}

        for state in self._config.states:
            state_table[state] = [default_state for col in xrange(256)]

        for i_trans in range(len(self._config.transitions) - 1, -1, -1):
            trans = self._config.transitions[i_trans]
            expr = self._config.events[trans.event]
            charList = self._expandExpression(expr)

            for c in charList:
                state_table[trans.src][ord(c)] = self._stateName(trans.dest)

        for state in self._config.states:
            transition_row = []
            for c in xrange(256):
                transition_row.append('    /* %06s */ {%s, NULL, NULL}' % (repr(chr(c)),
                                                          state_table[state][c]))

            out.append(self._generateStruct('%s_transition_%s' % (self._config.name.lower(), state),
                                             'xpfsm_transition',
                                             transition_row))
            out.append('\n')

        # Create the outer list, which map source states to input characters.
#       out.append('static const %s %s[] = {\n' % ('int *', self._config.name.lower() +
#                                                   '_state_transitions'))
#
#       row_list = ['  %s_transition_%s' %
#                    (self._config.name.lower(), row) for row in self._config.states]
#       out.append(',\n'.join(row_list))
#       out.append('\n};\n')

        out.append('%s %s[] = {\n' % ('xpfsm_state', self._config.name.lower() + '_states'))
        row_list = ['  { NULL, NULL, NULL, NULL, %s_transition_%s}' % (self._config.name.lower(), row) for row in self._config.states]
        out.append(',\n'.join(row_list))
        out.append('\n};\n')
        return ''.join(out)



def main():
    if len(sys.argv) != 2:
        print 'usage: xpgenfsm.py [config_file]'
        sys.exit(1)

    conf = xpfsm_config()
    conf.load(sys.argv[1])

    gen = xpfsm_generator(conf)
    gen.generate()

if __name__ == "__main__":
    main()
